/*
 * LocalClassLoader.java - ScriptME
 * 
 * Copyright (c) 2009 Cesar Henriques <cesar at alttab.com.ar>.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser Public License v2.1
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * 
 * Based on FESI Project
 * 
 * Contributors:
 * 	Jean-Marc Lugrin - initial API and implementation
 * 	Cesar Henriques <cesar at alttab.com.ar> - J2ME Porting and Extensions
 */
package org.scriptme.ecmascript.interpreter;

import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Hashtable;
import javax.microedition.lcdui.Image;

import org.scriptme.ecmascript.data.ESLoader;

/**
 * This code is largely inspired of Java Examples in a Nutshell. The loaders are
 * shared by all instance of the evaluator, they are therefore synchronized when
 * needed.
 */
public class LocalClassLoader {

	/** The Constant urlPrefix. */
	public final static String urlPrefix = "scriptme";

	/** The Constant resourceName. */
	public final static String resourceName = "scriptmeresource"; // must
																	// match
																	// class
																	// name

	/** The loaders by cookie. */
	private static Hashtable loadersByCookie = new Hashtable(); // loaders by
																// cookie

	/** The loaders by filename. */
	private static Hashtable loadersByFilename = new Hashtable(); // loaders
																	// by file
																	// name

	/** The Constant protocolPathProp. */
	private static final String protocolPathProp = "java.protocol.handler.pkgs";

	/** The cookie count. */
	private static int cookieCount = 1;

	/** The file sep. */
	private static char fileSep = System.getProperty("file.separator")
			.charAt(0);

	/** This is the directory from which the classes will be loaded. */
	private String myCookie;

	/**
	 * The constructor.
	 */
	public LocalClassLoader() {
		myCookie = "fcl" + cookieCount++;
		loadersByCookie.put(myCookie, this);
		if (ESLoader.isDebugLoader())
			System.out.println(" ** New class loader: " + this);
	}

	/**
	 * A convenience method that calls the 2-argument form of this method.
	 * 
	 * @param name
	 *            the name
	 * 
	 * @return the loaded class
	 * 
	 * @throws ClassNotFoundException
	 *             the class not found exception
	 * 
	 * @exception ClassNotFoundException
	 *                If class cannot be loaded
	 */
	public Class loadClass(String name) throws ClassNotFoundException {
		return loadClass(name, true);
	}

	/**
	 * This is one abstract method of ClassLoader that all subclasses must
	 * define. Its job is to load an array of bytes from somewhere and to pass
	 * them to defineClass(). If the resolve argument is true, it must also call
	 * resolveClass(), which will do things like verify the presence of the
	 * superclass. Because of this second step, this method may be called to
	 * load superclasses that are system classes, and it must take this into
	 * account.
	 * 
	 * @param resolve
	 *            True if class must be resolved
	 * @param classname
	 *            the classname
	 * 
	 * @return the loaded class
	 * 
	 * @throws ClassNotFoundException
	 *             the class not found exception
	 * 
	 * @exception ClassNotFoundException
	 *                If class cannot be loaded
	 */
	public Class loadClass(String classname, boolean resolve)
			throws ClassNotFoundException {
		return Class.forName(classname);
	}

	/**
	 * Gets the resource as stream.
	 * 
	 * @param name
	 *            the name
	 * 
	 * @return the resource as stream
	 */
	public InputStream getResourceAsStream(String name) {
		if (ESLoader.isDebugLoader())
			System.out.println(" ** getResourceAsStream: '" + name
					+ "' asked to: " + this);

		InputStream back = this.getClass().getResourceAsStream(name);
		if (back != null) {
			if (ESLoader.isDebugLoader())
				System.out.println(" ** getResourceAsStream(" + name
						+ ") is a system resource");
			return back;
		}
		return getLocalResourceAsStream(name);
	}

	/**
	 * Gets the resource buffer.
	 * 
	 * @param name
	 *            the name
	 * 
	 * @return the resource buffer
	 */
	private byte[] getResourceBuffer(String name) {
		byte[] buffer = null;

		if (ESLoader.isDebugLoader())
			System.out
					.println(" ** getResourceBuffer, resource '" + name + "'");

		try {
			DataInputStream stream = new DataInputStream(this.getClass()
					.getResourceAsStream(name));

			int length;

			while ((length = stream.available()) > 0) {
				buffer = new byte[length];
				stream.read(buffer);
			}

		} catch (IOException e) {
			if (ESLoader.isDebugLoader())
				System.out.println(" ** Error reading file: " + e);
			return null;
		}
		return buffer;
	}

	/**
	 * Gets the local resource.
	 * 
	 * @param name
	 *            the name
	 * 
	 * @return the local resource
	 */
	private Object getLocalResource(String name) {
		if (ESLoader.isDebugLoader())
			System.out.println(" ** getLocalResource, resource '" + name
					+ "' asked to: " + this);

		byte buf[] = getResourceBuffer(name);
		if (buf == null)
			return null;

		if (name.endsWith(".gif") || name.endsWith(".jpeg")
				|| name.endsWith(".png")) {
			try {
				Image image = Image.createImage(name);
				if (ESLoader.isDebugLoader())
					System.out
							.println(" ** Returning image resource: " + image);
				return image;
			} catch (IOException ex) {
				ex.printStackTrace();
				return null;
			}
		} else {
			ByteArrayInputStream s = new ByteArrayInputStream(buf);
			if (ESLoader.isDebugLoader())
				System.out.println(" ** Returning stream resource: " + s);
			return s;
		}
	}

	/**
	 * Gets the local resource as stream.
	 * 
	 * @param name
	 *            the name
	 * 
	 * @return the local resource as stream
	 */
	private InputStream getLocalResourceAsStream(String name) {
		if (ESLoader.isDebugLoader())
			System.out.println(" ** getLocalResourceAsStream,  resource '"
					+ name + "' asked to: " + this);

		// Get the stream of this file.
		InputStream inputStream = this.getClass().getResourceAsStream(name);

		if (ESLoader.isDebugLoader())
			System.out.println(" ** Resource found, returned as stream: "
					+ inputStream);
		return inputStream;
	}

	/**
	 * Gets the local resource as stream.
	 * 
	 * @param cookie
	 *            the cookie
	 * @param name
	 *            the name
	 * 
	 * @return the local resource as stream
	 */
	public static InputStream getLocalResourceAsStream(String cookie,
			String name) {
		if (ESLoader.isDebugLoader())
			System.out.println(" ** static getLocalResourceAsStream, cookie: "
					+ cookie + ", resource: " + name);
		LocalClassLoader cl = (LocalClassLoader) loadersByCookie.get(cookie);
		if (cl == null) {
			if (ESLoader.isDebugLoader())
				System.err.println(" @@ LocalClassLoader cookie: " + cookie
						+ " NOT FOUND !");
			return null;
		}
		if (ESLoader.isDebugLoader())
			System.out.println(" ** Classloader found: " + cl);

		return cl.getLocalResourceAsStream(name);
	}

	/**
	 * Gets the local resource.
	 * 
	 * @param cookie
	 *            the cookie
	 * @param name
	 *            the name
	 * 
	 * @return the local resource
	 */
	public static Object getLocalResource(String cookie, String name) {
		if (ESLoader.isDebugLoader())
			System.out.println(" ** static getLocalResource, cookie: " + cookie
					+ ", resource: " + name);
		LocalClassLoader cl = (LocalClassLoader) loadersByCookie.get(cookie);
		if (cl == null) {
			if (ESLoader.isDebugLoader())
				System.out.println(" @@ LocalClassLoader cookie: " + cookie
						+ " NOT FOUND !");
			return null;
		}
		if (ESLoader.isDebugLoader())
			System.out.println(" ** Classloader found: " + cl);

		return cl.getLocalResource(name);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#toString()
	 */
	public String toString() {
		return "LocalClassLoader[" + myCookie + "]";
	}

	static {
		// Add this protocol type to the http properties
		/*
		 * Properties newP = new Properties(System.getProperties());
		 * newP.put(protocolPathProp,
		 * newP.getProperty(protocolPathProp)+"|FESI.Interpreter");
		 * System.setProperties(newP);
		 */
	}
}
